package com.boot.mall.ware.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.boot.common.constant.MqDestinationConstant;
import com.boot.common.constant.StockLockConstant;
import com.boot.common.enume.OrderStatusEnum;
import com.boot.common.to.HasStockTo;
import com.boot.common.to.mq.StockDetailTo;
import com.boot.common.to.mq.StockLockedTo;
import com.boot.mall.ware.dao.WareOrderTaskDao;
import com.boot.mall.ware.dao.WareOrderTaskDetailDao;
import com.boot.mall.ware.entity.WareOrderTaskDetailEntity;
import com.boot.mall.ware.entity.WareOrderTaskEntity;
import com.boot.mall.ware.exception.NotStackException;
import com.boot.mall.ware.exception.OrderCancelException;
import com.boot.mall.ware.exception.StockLockedException;
import com.boot.mall.ware.feign.OrderFeignService;
import com.boot.mall.ware.mq.JmsProducerService;
import com.boot.mall.ware.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.boot.common.utils.PageUtils;

import com.boot.mall.ware.dao.WareSkuDao;
import com.boot.mall.ware.entity.WareSkuEntity;
import com.boot.mall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Resource
    private WareSkuDao wareSkuDao;
    @Resource
    private WareOrderTaskDetailDao wareOrderTaskDetailDao;
    @Resource
    private WareOrderTaskDao wareOrderTaskDao;
    @Autowired
    private JmsProducerService jmsProducerService;
    @Autowired
    private OrderFeignService orderFeignService;


    @Override
    public PageUtils pageSku(Map<String, Object> params) {
        String skuId = (String) params.get("skuId");
        String wareId = (String) params.get("wareId");
        long current = Integer.parseInt(params.get("page").toString());
        long limit = Integer.parseInt(params.get("limit").toString());
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(skuId)){
            queryWrapper.eq("sku_id",skuId);
        }
        if (!StringUtils.isEmpty(wareId)){
            queryWrapper.eq("ware_id",wareId);
        }
        Page<WareSkuEntity> page = new Page<>(current,limit);
        this.page(page,queryWrapper);
        // 结果封装
        PageUtils pageUtils = new PageUtils(page.getRecords(),(int)page.getTotal(),(int)page.getSize(),(int)current);
        return pageUtils;
    }

    // 判断sku是否含有库存
    @Override
    public List<HasStockTo> hasStock(List<Long> skuIds) {
        List<HasStockTo> collect = skuIds.stream().map(skuId -> {
            HasStockTo hasStockTo = new HasStockTo();
            Integer stock = baseMapper.hasStock(skuId);
            if (stock == null){
                hasStockTo.setHasStock(false);
            } else if (stock > 0) {
                hasStockTo.setHasStock(true);
            } else {
                hasStockTo.setHasStock(false);
            }
            hasStockTo.setSkuId(skuId);
            return hasStockTo;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 锁定库存
     * @param vo
     * @return
     */
    @Transactional(rollbackFor = NotStackException.class)
    @Override
    public Boolean lockSkuStock(WareSkuLockVo vo) {
        // TODO 1.按照订单的收货地址，找最近的的仓库，锁定库存

        // 2.查询每个商品在哪个仓库有库存
        List<OrderItemVo> locks = vo.getLocks();

        // 3.查询各个sku的库存数量
        List<SkuWareHasStockVo> wareStock = locks.stream().map(item -> {
            SkuWareHasStockVo hasStockVo = new SkuWareHasStockVo();
            hasStockVo.setSkuId(item.getSkuId());
            // 查询当前sku在哪些仓库中含有库存，以及库存数量
            List<SkuStockNum> stockNums = wareSkuDao.listSkuHasStockNum(item.getSkuId());
            hasStockVo.setWareStock(stockNums);
            return hasStockVo;
        }).collect(Collectors.toList());

        // 4.当查询所有的仓库库存都为null时，直接返回库存为空
        if (wareStock == null || wareStock.size() == 0){
            throw new NotStackException(1L);
        }

        // 5.查询每个订单项商品购买数量
        Map<Long, Integer> orderCount = locks.stream().collect(Collectors.toMap(OrderItemVo::getSkuId, OrderItemVo::getCount));

        // 6.锁定库存
        StockLockedTo lockedTo = new StockLockedTo();
        List<StockDetailTo> detailTos = new ArrayList<>();

        // 创建库存工作单
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskDao.insert(taskEntity);

        int totalNum = 0;
        // 全局变量判断是否库存锁定成功，当有一个锁定失败，则mark为false
        Boolean mark = true;
        // 遍历每一个sku的库存信息
        for (SkuWareHasStockVo stock : wareStock) {
            // 获取每个sku的在各个仓库的库存集合
            List<SkuStockNum> stockNums = stock.getWareStock();
            Long skuId = stock.getSkuId();      //skuId提取出来
            Integer count = orderCount.get(skuId);   //订单购买的sku数量
            // 判空
            if (stockNums != null && stockNums.size() > 0){
                // 遍历当前sku的各个仓库的库存信息
                for (SkuStockNum stockNum : stockNums) {
                    // 计算总的库存数量，总数量大于订单数量即可锁定库存
                    totalNum += stockNum.getNum();
                }
                if (count > totalNum){
                    // 当订单购买的数量大于总的库存
                    throw new NotStackException(skuId);
                }
                // 购买数量小于总的库存数量，对库存进行锁定
                for (SkuStockNum stockNum : stockNums) {
                    if (count == 0){
                        // 待锁定的库存为0，即全部锁定成功
                        break;
                    }
                    Long wareId = stockNum.getWareId(); //被锁仓库的id
                    Integer num = stockNum.getNum();    //可被锁定的数量，即剩余数量
                    if (count >= num){
                        // 被购买的数量大于当前仓库的总库存，但是又小于总的库存量，所以当前仓库剩余的数量全部锁定，剩余被锁定的数量减去已经锁定的数量
                        Long row = wareSkuDao.lockSkuStock(skuId, wareId, num); //锁定当前仓库全部的剩余库存
                        if (row == 0){
                            mark = false;
                        }else {
                            WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity(null,skuId,null,num,taskEntity.getId(),wareId, StockLockConstant.LOCKED);
                            StockDetailTo detailTo = saveWareTaskDetail(detailEntity);
                            detailTos.add(detailTo);    //添加到to对象中
                            count -= num;   //锁定成功，执行下一轮的判断锁定
                        }
                    }else {
                        // 被购买的数量小于当前仓库的总库存，直接锁
                        Long row = wareSkuDao.lockSkuStock(skuId, wareId, count);
                        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity(null,skuId,null,count,taskEntity.getId(),wareId, StockLockConstant.LOCKED);
                        StockDetailTo detailTo = saveWareTaskDetail(detailEntity);
                        detailTos.add(detailTo);    //添加到to对象中
                        if (row == 0){
                            mark = false;
                        }
                        count = 0;
                    }
                }
                if (!mark){
                    // 库存锁定失败
                    throw new NotStackException(skuId);
                }
            }
        }
        // 运行到此处没出现异常表明库存锁定成功，库存锁定成功之后给mq延迟发送消息
        lockedTo.setDetailToList(detailTos);
        lockedTo.setId(taskEntity.getId());
        lockedTo.setOrderSn(vo.getOrderSn());
        jmsProducerService.delaySendMessage(MqDestinationConstant.ORDER_QUEUE,lockedTo,60L * 10);
        return true;
    }

    /**
     * 解锁订单锁定的库存
     * @param orderSn
     * @param status
     */
    @Override
    @Transactional(rollbackFor = {StockLockedException.class,OrderCancelException.class})   //出现异常就回滚
    public void releaseStock(String orderSn, Integer status){
        // 1.查询当前订单保存的任务单
        WareOrderTaskEntity orderTask = wareOrderTaskDao.selectOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        Long taskId = orderTask.getId();    //当前任务单的任务id
        // 2.查询任务单的详细任务，即sku在哪个仓库锁定多少件库存信息
        List<WareOrderTaskDetailEntity> taskDetails = wareOrderTaskDetailDao.selectList(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", taskId));
        for (WareOrderTaskDetailEntity task : taskDetails) {
            // 3.解锁库存
            Long row = wareSkuDao.releaseLock(task.getSkuNum(), task.getSkuId(), task.getWareId());
            if (row == 0){
                //解锁失败，抛出异常
                throw new StockLockedException();
            }
            // 4.更改任务单的状态为已解锁
            task.setLockStatus(StockLockConstant.UNLOCKED);
            wareOrderTaskDetailDao.updateById(task);
        }
        // “待付款”状态还需取消订单操作
        if (status == OrderStatusEnum.CREATE_NEW.getCode()){
            Boolean res = orderFeignService.cancelOrder(orderSn);
            if (!res){
                // 当返回结果为false时，表示修改订单状态失败，抛出异常
                throw new OrderCancelException();
            }
            System.out.println("订单取消成功");
        }
        // 运行到此处没有发生异常
    }

    /**
     * 保存工作项单
     * @param detailEntity
     * @return
     */
    private StockDetailTo saveWareTaskDetail(WareOrderTaskDetailEntity detailEntity) {
        wareOrderTaskDetailDao.insert(detailEntity);    //保存
        StockDetailTo detailTo = new StockDetailTo();
        BeanUtils.copyProperties(detailEntity, detailTo);
        return detailTo;
    }
}